www.gusucode.com > VC++ P2P下载软件源代码-源码程序 > VC++ P2P下载软件源代码-源码程序\code\windows\QueueFrame.cpp

    //Download by http://www.NewXing.com
/* 
 * Copyright (C) 2001-2003 Jacek Sieka, j_s@telia.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdafx.h"
#include "../client/DCPlusPlus.h"
#include "Resource.h"

#include "QueueFrame.h"
#include "SearchFrm.h"
#include "PrivateFrame.h"

#include "../client/SimpleXML.h"
#include "../client/StringTokenizer.h"
#include "../client/ShareManager.h"

QueueFrame* QueueFrame::frame = NULL;

#define FILE_LIST_NAME "File Lists"

int QueueFrame::columnIndexes[] = { COLUMN_TARGET, COLUMN_STATUS, COLUMN_SIZE, COLUMN_PRIORITY,
COLUMN_USERS, COLUMN_PATH, COLUMN_ERRORS };

int QueueFrame::columnSizes[] = { 200, 300, 75, 75, 200, 200, 200 };

static ResourceManager::Strings columnNames[] = { ResourceManager::FILENAME, ResourceManager::STATUS, ResourceManager::SIZE, 
ResourceManager::PRIORITY, ResourceManager::USERS, ResourceManager::PATH, ResourceManager::ERRORS };

LRESULT QueueFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
	dcassert(frame == NULL);
	frame = this;

	showTree = BOOLSETTING(QUEUEFRAME_SHOW_TREE);
	
	CreateSimpleStatusBar(ATL_IDS_IDLEMESSAGE, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP);
	ctrlStatus.Attach(m_hWndStatusBar);
	
	ctrlQueue.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 
		WS_HSCROLL | WS_VSCROLL | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS, WS_EX_CLIENTEDGE, IDC_QUEUE);

	if(BOOLSETTING(FULL_ROW_SELECT)) {
		ctrlQueue.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP);
	}
	else {
		ctrlQueue.SetExtendedListViewStyle(LVS_EX_HEADERDRAGDROP);
	}

	ctrlDirs.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
		TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP, 
		 WS_EX_CLIENTEDGE, IDC_DIRECTORIES);
	
	ctrlDirs.SetImageList(WinUtil::fileImages, TVSIL_NORMAL);
	ctrlQueue.SetImageList(WinUtil::fileImages, LVSIL_SMALL);
	
	m_nProportionalPos = 2500;
	SetSplitterPanes(ctrlDirs.m_hWnd, ctrlQueue.m_hWnd);

	// Create listview columns
	WinUtil::splitTokens(columnIndexes, SETTING(QUEUEFRAME_ORDER), COLUMN_LAST);
	WinUtil::splitTokens(columnSizes, SETTING(QUEUEFRAME_WIDTHS), COLUMN_LAST);
	
	for(int j=0; j<COLUMN_LAST; j++) {
		int fmt = (j == COLUMN_SIZE) ? LVCFMT_RIGHT : LVCFMT_LEFT;
		ctrlQueue.InsertColumn(j, CSTRING_I(columnNames[j]), fmt, columnSizes[j], j);
	}
	
	ctrlQueue.SetColumnOrderArray(COLUMN_LAST, columnIndexes);
	
	ctrlQueue.SetBkColor(WinUtil::bgColor);
	ctrlQueue.SetTextBkColor(WinUtil::bgColor);
	ctrlQueue.SetTextColor(WinUtil::textColor);

	ctrlDirs.SetBkColor(WinUtil::bgColor);
	ctrlDirs.SetTextColor(WinUtil::textColor);

	ctrlShowTree.Create(ctrlStatus.m_hWnd, rcDefault, "+/-", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
	ctrlShowTree.SetButtonStyle(BS_AUTOCHECKBOX, false);
	ctrlShowTree.SetCheck(showTree);
	showTreeContainer.SubclassWindow(ctrlShowTree.m_hWnd);
	
	singleMenu.CreatePopupMenu();
	multiMenu.CreatePopupMenu();
	browseMenu.CreatePopupMenu();
	removeMenu.CreatePopupMenu();
	pmMenu.CreatePopupMenu();
	priorityMenu.CreatePopupMenu();
	dirMenu.CreatePopupMenu();
	readdMenu.CreatePopupMenu();

	singleMenu.AppendMenu(MF_STRING, IDC_SEARCH_ALTERNATES, CSTRING(SEARCH_FOR_ALTERNATES));
	singleMenu.AppendMenu(MF_STRING, IDC_MOVE, CSTRING(MOVE));
	singleMenu.AppendMenu(MF_POPUP, (UINT)(HMENU)priorityMenu, CSTRING(SET_PRIORITY));
	singleMenu.AppendMenu(MF_POPUP, (UINT)(HMENU)browseMenu, CSTRING(GET_FILE_LIST));
	singleMenu.AppendMenu(MF_POPUP, (UINT)(HMENU)pmMenu, CSTRING(SEND_PRIVATE_MESSAGE));
	singleMenu.AppendMenu(MF_POPUP, (UINT)(HMENU)readdMenu, CSTRING(READD_SOURCE));
	singleMenu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)NULL);
	singleMenu.AppendMenu(MF_POPUP, (UINT)(HMENU)removeMenu, CSTRING(REMOVE_SOURCE));
	singleMenu.AppendMenu(MF_STRING, IDC_REMOVE, CSTRING(REMOVE));

	multiMenu.AppendMenu(MF_POPUP, (UINT)(HMENU)priorityMenu, CSTRING(SET_PRIORITY));
	multiMenu.AppendMenu(MF_STRING, IDC_MOVE, CSTRING(MOVE));
	multiMenu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)NULL);
	multiMenu.AppendMenu(MF_STRING, IDC_REMOVE, CSTRING(REMOVE));
	
	priorityMenu.AppendMenu(MF_STRING, IDC_PRIORITY_PAUSED, CSTRING(PAUSED));
	priorityMenu.AppendMenu(MF_STRING, IDC_PRIORITY_LOWEST, CSTRING(LOWEST));
	priorityMenu.AppendMenu(MF_STRING, IDC_PRIORITY_LOW, CSTRING(LOW));
	priorityMenu.AppendMenu(MF_STRING, IDC_PRIORITY_NORMAL, CSTRING(NORMAL));
	priorityMenu.AppendMenu(MF_STRING, IDC_PRIORITY_HIGH, CSTRING(HIGH));
	priorityMenu.AppendMenu(MF_STRING, IDC_PRIORITY_HIGHEST, CSTRING(HIGHEST));

	dirMenu.AppendMenu(MF_POPUP, (UINT)(HMENU)priorityMenu, CSTRING(SET_PRIORITY));
	dirMenu.AppendMenu(MF_STRING, IDC_MOVE, CSTRING(MOVE));
	dirMenu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)NULL);
	dirMenu.AppendMenu(MF_STRING, IDC_REMOVE, CSTRING(REMOVE));

	SetWindowText(CSTRING(DOWNLOAD_QUEUE));

	addQueueList(QueueManager::getInstance()->lockQueue());
	QueueManager::getInstance()->unlockQueue();
	QueueManager::getInstance()->addListener(this);

	memset(statusSizes, 0, sizeof(statusSizes));
	statusSizes[0] = 16;
	ctrlStatus.SetParts(6, statusSizes);
	updateStatus();

	bHandled = FALSE;
	return 1;
}

QueueFrame::StringListInfo::StringListInfo(QueueItem* qi) {

	columns[COLUMN_TARGET] = qi->getTargetFileName();
	columns[COLUMN_SIZE] = (qi->getSize() == -1) ? STRING(UNKNOWN) : Util::formatBytes(qi->getSize());
	string tmp;
	
	int online = 0;
	QueueItem::Source::Iter j;
	for(j = qi->getSources().begin(); j != qi->getSources().end(); ++j) {
		if(tmp.size() > 0)
			tmp += ", ";
		
		QueueItem::Source::Ptr sr = *j;
		if(sr->getUser()->isOnline())
			online++;
		
		tmp += sr->getUser()->getFullNick();
	}
	columns[COLUMN_USERS] = tmp.empty() ? STRING(NO_USERS) : tmp;
	tmp = Util::emptyString;
	for(j = qi->getBadSources().begin(); j != qi->getBadSources().end(); ++j) {
		QueueItem::Source::Ptr sr = *j;
		if(!sr->isSet(QueueItem::Source::FLAG_REMOVED)) {
			if(tmp.size() > 0)
				tmp += ", ";
			tmp += sr->getUser()->getNick();
			tmp += " (";
			if(sr->isSet(QueueItem::Source::FLAG_FILE_NOT_AVAILABLE)) {
				tmp += STRING(FILE_NOT_AVAILABLE);
			} else if(sr->isSet(QueueItem::Source::FLAG_PASSIVE)) {
				tmp += STRING(PASSIVE_USER);
			} else if(sr->isSet(QueueItem::Source::FLAG_ROLLBACK_INCONSISTENCY)) {
				tmp += STRING(ROLLBACK_INCONSISTENCY);
			} else if(sr->isSet(QueueItem::Source::FLAG_CRC_FAILED)) {
				tmp += STRING(SFV_INCONSISTENCY);
			}
			tmp += ')';
		}
	}
	columns[COLUMN_ERRORS] = tmp.empty() ? STRING(NO_ERRORS) : tmp;
	
	if(qi->getStatus() == QueueItem::STATUS_WAITING) {
		
		char buf[64];
		if(online > 0) {
			
			if(qi->getSources().size() == 1) {
				columns[COLUMN_STATUS] = STRING(WAITING_USER_ONLINE);
			} else {
				sprintf(buf, CSTRING(WAITING_USERS_ONLINE), online, qi->getSources().size());
				columns[COLUMN_STATUS] = buf;
			}
		} else {
			if(qi->getSources().size() == 0) {
				columns[COLUMN_STATUS] = STRING(NO_USERS_TO_DOWNLOAD_FROM);
			} else if(qi->getSources().size() == 1) {
				columns[COLUMN_STATUS] = STRING(USER_OFFLINE);
			} else if(qi->getSources().size() == 2) {
				columns[COLUMN_STATUS] = STRING(BOTH_USERS_OFFLINE);
			} else if(qi->getSources().size() == 3) {
				columns[COLUMN_STATUS] = STRING(ALL_3_USERS_OFFLINE);
			} else if(qi->getSources().size() == 4) {
				columns[COLUMN_STATUS] = STRING(ALL_4_USERS_OFFLINE);
			} else {
				sprintf(buf, CSTRING(ALL_USERS_OFFLINE), qi->getSources().size());
				columns[COLUMN_STATUS] = buf;
			}
		}
	} else if(qi->getStatus() == QueueItem::STATUS_RUNNING) {
		columns[COLUMN_STATUS] = STRING(RUNNING);
	} 
	
	switch(qi->getPriority()) {
	case QueueItem::PAUSED: columns[COLUMN_PRIORITY] = STRING(PAUSED); break;
	case QueueItem::LOWEST: columns[COLUMN_PRIORITY] = STRING(LOWEST); break;
	case QueueItem::LOW: columns[COLUMN_PRIORITY] = STRING(LOW); break;
	case QueueItem::NORMAL: columns[COLUMN_PRIORITY] = STRING(NORMAL); break;
	case QueueItem::HIGH: columns[COLUMN_PRIORITY] = STRING(HIGH); break;
	case QueueItem::HIGHEST: columns[COLUMN_PRIORITY] = STRING(HIGHEST); break;
	default: dcassert(0); break;
	}
	columns[COLUMN_PATH] = qi->getTarget();
}

void QueueFrame::onQueueAdded(QueueItem* aQI) {
	QueueItem* qi = new QueueItem(*aQI);
	{
		Lock l(cs);
		dcassert(queue.find(aQI) == queue.end());
		queue[aQI] = qi;
	}

	speak(ADD_ITEM,	qi);
}

void QueueFrame::addQueueItem(QueueItem* qi) {
	if(!qi->isSet(QueueItem::FLAG_USER_LIST)) {
		queueSize+=qi->getSize();
	}
	queueItems++;
	dirty = true;
	
	string dir = Util::getFilePath(qi->getTarget());
	
	bool updateDir = (directories.find(dir) == directories.end());
	directories.insert(make_pair(dir, qi));
	
	if(updateDir) {
		addDirectory(dir, qi->isSet(QueueItem::FLAG_USER_LIST));
	} 
	if(!showTree || (curDir == dir)) {
		StringListInfo sli(qi);
		StringList l;
		
		for(int j = 0; j < COLUMN_LAST; j++) {
			l.push_back(sli.columns[j]);
		}
		dcassert(ctrlQueue.find((LPARAM)qi) == -1);
		ctrlQueue.insert(l, WinUtil::getIconIndex(qi->getTarget()), (LPARAM)qi);
	}
}

void QueueFrame::addQueueList(const QueueItem::StringMap& li) {
	ctrlQueue.SetRedraw(FALSE);
	ctrlDirs.SetRedraw(FALSE);
	for(QueueItem::StringMap::const_iterator j = li.begin(); j != li.end(); ++j) {
		QueueItem* aQI = j->second;
		QueueItem* qi = new QueueItem(*aQI);
		dcassert(queue.find(aQI) == queue.end());
		queue[aQI] = qi;
		addQueueItem(qi);
	}
	ctrlQueue.SetRedraw(TRUE);
	ctrlQueue.resort();
	ctrlDirs.SetRedraw(TRUE);
	ctrlDirs.Invalidate();
}

HTREEITEM QueueFrame::addDirectory(const string& dir, bool isFileList /* = false */) {
	TVINSERTSTRUCT tvi;
	tvi.hInsertAfter = TVI_SORT;
	tvi.item.mask = TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_TEXT;
	tvi.item.iImage = tvi.item.iSelectedImage = WinUtil::getDirIconIndex();

	if(isFileList) {
		// We assume we haven't added it yet, and that all filelists go to the same
		// directory...
		dcassert(fileLists == NULL);
		tvi.hParent = NULL;
		tvi.item.pszText = FILE_LIST_NAME;
		tvi.item.lParam = (LPARAM) new string(dir);
		fileLists = ctrlDirs.InsertItem(&tvi);
		return fileLists;
	} 

	// More complicated, we have to find the last available tree item and then see...
	string::size_type i = 0;
	string::size_type j;

	HTREEITEM next = ctrlDirs.GetRootItem();
	HTREEITEM parent = NULL;

	if((next != NULL) && (next == fileLists)) {
		next = ctrlDirs.GetNextSiblingItem(next);
	}

	if(next == NULL) {
		// First addition, set commonStart to the dir minus the last part...
		i = dir.rfind('\\', dir.length()-2);
		if(i != string::npos) {
			commonStart = dir.substr(0, i+1);
			string name = commonStart.substr(0, commonStart.length() - 1);
			tvi.hParent = NULL;
			tvi.item.pszText = const_cast<char*>(name.c_str());
			tvi.item.lParam = (LPARAM)new string(commonStart);
			parent = ctrlDirs.InsertItem(&tvi);
			next = NULL;
			i++;
		} else {
			// No commonStart...
			i = 0;
		}
	} else {
		// Check if commonStart is the same as this dir start...
		if(commonStart.empty()) {
			// nothing...
		} else if(Util::strnicmp(dir, commonStart, commonStart.length()) == 0) {
			// Ok, np, commonstart is equal...
			i = commonStart.length();
			dcassert(next != NULL);
			parent = next;
			next = ctrlDirs.GetChildItem(next);
		} else {
			string oldCommon = commonStart;
			i = 0;
			for(;;) {
				j = dir.find('\\', i);
				if(j == string::npos) {
					commonStart = dir.substr(0, i);
					break;
				}
				if(Util::strnicmp(dir, commonStart, j) == 0) {
					i = j + 1;
				} else {
					commonStart = dir.substr(0, i);
					break;
				}
			}
			if(commonStart.empty()) {
				// We insert the first part, if not, addDirectory will return the old commonStart
				// Since this gets sorted before oldCommon, it should work =)
				i = oldCommon.find('\\');
				dcassert(i != string::npos);
				string* tmp = new string(oldCommon.substr(0, i+1));
				string name = tmp->substr(0, tmp->length() - 1);
				tvi.hParent = NULL;
				tvi.item.pszText = const_cast<char*>(name.c_str());
				tvi.item.lParam = (LPARAM)tmp;
				ctrlDirs.InsertItem(&tvi);
				HTREEITEM ht = addDirectory(oldCommon);
				HTREEITEM ht2 = ctrlDirs.GetChildItem(next);
				while(ht2 != NULL) {
					moveNode(ht2, ht);
					ht2 = ctrlDirs.GetChildItem(next);
				}
				delete (string*)ctrlDirs.GetItemData(next);
				ctrlDirs.DeleteItem(next);
				next = ctrlDirs.GetRootItem();
				if((next != NULL) && (next == fileLists)) {
					next = ctrlDirs.GetNextSiblingItem(next);
				}
				i = 0;
			} else {
				string name = commonStart.substr(0, commonStart.length() - 1);
				tvi.hParent = NULL;
				tvi.item.pszText = const_cast<char*>(name.c_str());
				tvi.item.lParam = (LPARAM)new string(commonStart);
				parent = ctrlDirs.InsertItem(&tvi);
				HTREEITEM ht = addDirectory(oldCommon);
				HTREEITEM ht2 = ctrlDirs.GetChildItem(next);
				while(ht2 != NULL) {
					moveNode(ht2, ht);
					ht2 = ctrlDirs.GetChildItem(next);
				}
				delete (string*)ctrlDirs.GetItemData(next);
				ctrlDirs.DeleteItem(next);

				next = ctrlDirs.GetChildItem(parent);
				i = commonStart.length();
			}
		}
	}


	while( i < dir.length() ) {
		if(next == fileLists) {
			next = ctrlDirs.GetNextSiblingItem(next);
		}
		while(next != NULL) {
			const string& n = getDir(next);
			if(Util::strnicmp(n.c_str()+i, dir.c_str()+i, n.length()-i) == 0) {
				// Found a part, we assume it's the best one we can find...
				i = n.length();
				
				parent = next;
				next = ctrlDirs.GetChildItem(next);
				break;
			}
			next = ctrlDirs.GetNextSiblingItem(next);
		}

		if(next == NULL) {
			// We didn't find it, add...
			j = dir.find('\\', i);
			dcassert(j != string::npos);
			string name = dir.substr(i, j-i);
			tvi.hParent = parent;
			tvi.item.pszText = const_cast<char*>(name.c_str());
			tvi.item.lParam = (LPARAM) new string(dir.substr(0, j+1));
			
			parent = ctrlDirs.InsertItem(&tvi);
			
			if(directories.find(getDir(parent)) == directories.end()) {
				ctrlDirs.Expand(ctrlDirs.GetParentItem(parent), TVE_EXPAND);
			}
			i = j + 1;
		}
	}
	return parent;
}

void QueueFrame::removeDirectory(const string& dir, bool isFileList /* = false */) {

	// First, find the last name available
	string::size_type i = 0;

	HTREEITEM next = ctrlDirs.GetRootItem();
	HTREEITEM parent = NULL;
	
	if(isFileList) {
		dcassert(fileLists != NULL);
		delete (string*)ctrlDirs.GetItemData(fileLists);
		ctrlDirs.DeleteItem(fileLists);
		fileLists = NULL;
		return;
	} else {
		while(i < dir.length()) {
			while(next != NULL) {
				if(next == fileLists) {
					next = ctrlDirs.GetNextSiblingItem(next);
					continue;
				}
				const string& n = getDir(next);
				if(Util::strnicmp(n.c_str()+i, dir.c_str()+i, n.length()-i) == 0) {
					// Match!
					parent = next;
					next = ctrlDirs.GetChildItem(next);
					i = n.length();
					break;
				}
				next = ctrlDirs.GetNextSiblingItem(next);
			}
			if(next == NULL)
				break;
		}
	}

	next = parent;

	while((ctrlDirs.GetChildItem(next) == NULL) && (directories.find(getDir(next)) == directories.end())) {
		delete (string*)ctrlDirs.GetItemData(next);
		parent = ctrlDirs.GetParentItem(next);
		
		ctrlDirs.DeleteItem(next);
		if(parent == NULL)
			break;
		next = parent;
	}
}

void QueueFrame::removeDirectories(HTREEITEM ht) {
	HTREEITEM next = ctrlDirs.GetChildItem(ht);
	while(next != NULL) {
		removeDirectories(next);
		next = ctrlDirs.GetNextSiblingItem(ht);
	}
	delete (string*)ctrlDirs.GetItemData(ht);
	ctrlDirs.DeleteItem(ht);
}

void QueueFrame::onQueueRemoved(QueueItem* aQI) {
	QueueItem* qi = NULL;
	{
		Lock l(cs);
		QueueIter i = queue.find(aQI);
		dcassert(i != queue.end());
		qi = i->second;
		queue.erase(i);

		if(!aQI->isSet(QueueItem::FLAG_USER_LIST)) {
			queueSize-=aQI->getSize();
			dcassert(queueSize >= 0);
		}
		queueItems--;
		dcassert(queueItems >= 0);
		dirty = true;
	}

	speak(REMOVE_ITEM, qi);
}

void QueueFrame::onQueueMoved(QueueItem* aQI) {
	QueueItem* qi = NULL;
	QueueItem* qi2 = new QueueItem(*aQI);
	{
		Lock l(cs);
		dcassert(queue.find(aQI) != queue.end());
		QueueIter i = queue.find(aQI);
		qi = i->second;
		i->second = qi2;
	}
	
	speak(REMOVE_ITEM, qi);
	speak(ADD_ITEM,	qi2);
}

void QueueFrame::onQueueUpdated(QueueItem* aQI) {
	QueueItem* qi = NULL;
	{
		Lock l(cs);
		dcassert(queue.find(aQI) != queue.end());
		qi = queue[aQI];
		{
			for(QueueItem::Source::Iter i = qi->getSources().begin(); i != qi->getSources().end(); ++i) {
				delete *i;
			}
			qi->getSources().clear();
			for(QueueItem::Source::Iter j = aQI->getSources().begin(); j != aQI->getSources().end(); ++j) {
				qi->getSources().push_back(new QueueItem::Source(*(*j)));
			}
		}
		{
			for(QueueItem::Source::Iter i = qi->getBadSources().begin(); i != qi->getBadSources().end(); ++i) {
				delete *i;
			}
			qi->getBadSources().clear();
			for(QueueItem::Source::Iter j = aQI->getBadSources().begin(); j != aQI->getBadSources().end(); ++j) {
				qi->getBadSources().push_back(new QueueItem::Source(*(*j)));
			}
		}

		qi->setPriority(aQI->getPriority());
		qi->setStatus(aQI->getStatus());
	}
	
	speak(SET_TEXT, qi);
}

LRESULT QueueFrame::onSpeaker(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) {
	Lock l(cs);
	spoken = false;

	for(TaskIter ti = tasks.begin(); ti != tasks.end(); ++ti) {
		if(ti->first == ADD_ITEM) {
			QueueItem* qi = (QueueItem*)ti->second;
			addQueueItem(qi);

			updateStatus();
		} else if(ti->first == REMOVE_ITEM) {
			QueueItem* qi = (QueueItem*)ti->second;
			string dir = Util::getFilePath(qi->getTarget());
			
			if(!showTree || (dir == curDir)) {
				dcassert(ctrlQueue.find((LPARAM)qi) != -1);
				ctrlQueue.DeleteItem(ctrlQueue.find((LPARAM)qi));
			}
			
			pair<DirectoryIter, DirectoryIter> i = directories.equal_range(dir);
			DirectoryIter j;
			for(j = i.first; j != i.second; ++j) {
				if(j->second == qi)
					break;
			}
			dcassert(j != i.second);
			directories.erase(j);
			if(directories.count(dir) == 0) {
				removeDirectory(dir, qi->isSet(QueueItem::FLAG_USER_LIST));
				if(curDir == dir)
					curDir = Util::emptyString;
			}
			
			delete qi;
			updateStatus();
			setDirty();
		} else if(ti->first == SET_TEXT) {
			QueueItem* qi = (QueueItem*)ti->second;
			
			string dir = Util::getFilePath(qi->getTarget());
			if(!showTree || (dir == curDir) ) {
				StringListInfo sli(qi);
				int n = ctrlQueue.find((LPARAM)qi);
				dcassert(n != -1);
				ctrlQueue.SetRedraw(FALSE);
				for(int i = 0; i < COLUMN_LAST; i++) {
					if(!sli.columns[i].empty()) {
						ctrlQueue.SetItemText(n, i, sli.columns[i].c_str());
					}
				}
				ctrlQueue.SetRedraw(TRUE);
			}
		}
	}
	if(tasks.size() > 0) {
		tasks.clear();
	}

	return 0;
}

void QueueFrame::moveSelected() {

	int n = ctrlQueue.GetSelectedCount();
	if(n == 1) {
		// Single file, get the full filename and move...
		QueueItem* qi = (QueueItem*)ctrlQueue.GetItemData(ctrlQueue.GetNextItem(-1, LVNI_SELECTED));
		string name = qi->getTarget();
		if(WinUtil::browseFile(name, m_hWnd, true, Util::getFilePath(name))) {
			QueueManager::getInstance()->move(qi->getTarget(), name);
		}
	} else if(n > 1) {
		string name;
		if(showTree) {
			name = curDir;
		}

		if(WinUtil::browseDirectory(name, m_hWnd)) {
			int i = -1;
			while( (i = ctrlQueue.GetNextItem(i, LVNI_SELECTED)) != -1) {
				QueueItem* qi = (QueueItem*)ctrlQueue.GetItemData(i);
				QueueManager::getInstance()->move(qi->getTarget(), name + qi->getTargetFileName());
			}			
		}
	}
}

void QueueFrame::moveSelectedDir() {
	if(ctrlDirs.GetSelectedItem() == NULL)
		return;

	dcassert(!curDir.empty());
	string name = curDir;
	
	if(WinUtil::browseDirectory(name, m_hWnd)) {
		moveDir(ctrlDirs.GetSelectedItem(), name);
	}
}

void QueueFrame::moveDir(HTREEITEM ht, const string& target) {
	HTREEITEM next = ctrlDirs.GetChildItem(ht);
	while(next != NULL) {
		moveDir(next, target);
		next = ctrlDirs.GetNextSiblingItem(next);
	}
	string* s = (string*)ctrlDirs.GetItemData(ht);
	string name = target + s->substr(curDir.length());
	
	DirectoryPair p = directories.equal_range(*s);
	
	for(DirectoryIter i = p.first; i != p.second; ++i) {
		QueueItem* qi = i->second;
		QueueManager::getInstance()->move(qi->getTarget(), name + qi->getTargetFileName());
	}			
}

LRESULT QueueFrame::onContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) {
	RECT rc;                    // client area of window 
	POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };        // location of mouse click 
	
	// Get the bounding rectangle of the client area. 
	ctrlQueue.GetClientRect(&rc);
	ctrlQueue.ScreenToClient(&pt); 
	if (PtInRect(&rc, pt) && ctrlQueue.GetSelectedCount() > 0) { 
		usingDirMenu = false;
		CMenuItemInfo mi;
		
		while(browseMenu.GetMenuItemCount() > 0) {
			browseMenu.RemoveMenu(0, MF_BYPOSITION);
		}
		while(removeMenu.GetMenuItemCount() > 0) {
			removeMenu.RemoveMenu(0, MF_BYPOSITION);
		}
		while(pmMenu.GetMenuItemCount() > 0) {
			pmMenu.RemoveMenu(0, MF_BYPOSITION);
		}
		while(readdMenu.GetMenuItemCount() > 0) {
			readdMenu.RemoveMenu(0, MF_BYPOSITION);
		}

		ctrlQueue.ClientToScreen(&pt);
		
		if(ctrlQueue.GetSelectedCount() == 1) {
			LVITEM lvi;
			lvi.iItem = ctrlQueue.GetNextItem(-1, LVNI_SELECTED);
			lvi.iSubItem = 0;
			lvi.mask = LVIF_IMAGE | LVIF_PARAM;
			
			ctrlQueue.GetItem(&lvi);
			menuItems = 0;
			
			QueueItem::Source::Iter i;
			QueueItem* q = (QueueItem*)lvi.lParam;
			for(i = q->getSources().begin(); i != q->getSources().end(); ++i) {

				mi.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA;
				mi.fType = MFT_STRING;
				mi.dwTypeData = (LPSTR)(*i)->getUser()->getNick().c_str();
				mi.dwItemData = (DWORD)*i;
				mi.wID = IDC_BROWSELIST + menuItems;
				browseMenu.InsertMenuItem(menuItems, TRUE, &mi);
				mi.wID = IDC_REMOVE_SOURCE + menuItems;
				removeMenu.InsertMenuItem(menuItems, TRUE, &mi);
				if((*i)->getUser()->isOnline()) {
					mi.wID = IDC_PM + menuItems;
					pmMenu.InsertMenuItem(menuItems, TRUE, &mi);
				}
				menuItems++;
			}
			readdItems = 0;
			for(i = q->getBadSources().begin(); i != q->getBadSources().end(); ++i) {
				mi.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA;
				mi.fType = MFT_STRING;
				mi.dwTypeData = (LPSTR)(*i)->getUser()->getNick().c_str();
				mi.dwItemData = (DWORD)*i;
				mi.wID = IDC_READD + readdItems;
				readdMenu.InsertMenuItem(readdItems, TRUE, &mi);
				readdItems++;
			}

			singleMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, m_hWnd);
		} else {
			multiMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, m_hWnd);
		}
		
		return TRUE; 
	}

	ctrlQueue.ClientToScreen(&pt);

	ctrlDirs.GetClientRect(&rc);
	ctrlDirs.ScreenToClient(&pt);

	if (PtInRect(&rc, pt) && ctrlDirs.GetSelectedItem() != NULL) { 
		usingDirMenu = true;
		// Strange, windows doesn't change the selection on right-click... (!)
		UINT a = 0;
		HTREEITEM ht = ctrlDirs.HitTest(pt, &a);
		if(ht != NULL && ht != ctrlDirs.GetSelectedItem())
			ctrlDirs.SelectItem(ht);
		
		ctrlDirs.ClientToScreen(&pt);
		dirMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, m_hWnd);
	
		return TRUE;
	}

	return FALSE; 
}

LRESULT QueueFrame::onSearchAlternates(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	if(ctrlQueue.GetSelectedCount() == 1) {
		string tmp;

		int i = ctrlQueue.GetNextItem(-1, LVNI_SELECTED);
		QueueItem* qi = (QueueItem*)ctrlQueue.GetItemData(i);
		
		StringList tok = StringTokenizer(SearchManager::clean(qi->getTargetFileName()), ' ').getTokens();
		
		for(StringIter si = tok.begin(); si != tok.end(); ++si) {
			bool found = false;
			
			for(StringIter j = searchFilter.begin(); j != searchFilter.end(); ++j) {
				if(Util::stricmp(si->c_str(), j->c_str()) == 0) {
					found = true;
				}
			}
			
			if(!found && !si->empty()) {
				tmp += *si + ' ';
			}
		}
		if(!tmp.empty()) {
			SearchFrame* pChild = new SearchFrame();
			pChild->setTab(getTab());
			bool bigFile = (qi->getSize() > 10*1024*1024);
			if(bigFile) {
				pChild->setInitial(tmp, qi->getSize()-1, SearchManager::SIZE_ATLEAST, ShareManager::getInstance()->getType(qi->getTargetFileName()));
			} else {
				pChild->setInitial(tmp, qi->getSize()+1, SearchManager::SIZE_ATMOST, ShareManager::getInstance()->getType(qi->getTargetFileName()));
			}
			pChild->CreateEx(m_hWndMDIClient);
			
		}
	} 
	
	return 0;
}

LRESULT QueueFrame::onBrowseList(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	
	if(ctrlQueue.GetSelectedCount() == 1) {
		CMenuItemInfo mi;
		mi.fMask = MIIM_DATA;
		
		browseMenu.GetMenuItemInfo(wID, FALSE, &mi);
		QueueItem::Source* s = (QueueItem::Source*)mi.dwItemData;
		try {
			QueueManager::getInstance()->addList(s->getUser());
		} catch(const Exception&) {
		}
	}
	return 0;
}

LRESULT QueueFrame::onReadd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	
	if(ctrlQueue.GetSelectedCount() == 1) {
		int i = ctrlQueue.GetNextItem(-1, LVNI_SELECTED);
		QueueItem* q = (QueueItem*)ctrlQueue.GetItemData(i);

		CMenuItemInfo mi;
		mi.fMask = MIIM_DATA;
		
		readdMenu.GetMenuItemInfo(wID, FALSE, &mi);
		QueueItem::Source* s = (QueueItem::Source*)mi.dwItemData;
		try {
			QueueManager::getInstance()->add(s->getPath(), q->getSize(), s->getUser(), q->getTarget());
		} catch(const Exception& e) {
			ctrlStatus.SetText(0, e.getError().c_str());
		}
	}
	return 0;
}

LRESULT QueueFrame::onRemoveSource(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	
	if(ctrlQueue.GetSelectedCount() == 1) {
		int i = ctrlQueue.GetNextItem(-1, LVNI_SELECTED);
		QueueItem* q = (QueueItem*)ctrlQueue.GetItemData(i);
		CMenuItemInfo mi;
		mi.fMask = MIIM_DATA;
		
		removeMenu.GetMenuItemInfo(wID, FALSE, &mi);
		QueueItem::Source* s = (QueueItem::Source*)mi.dwItemData;
		QueueManager::getInstance()->removeSource(q->getTarget(), s->getUser(), QueueItem::Source::FLAG_REMOVED);
	}
	return 0;
}

LRESULT QueueFrame::onPM(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	
	if(ctrlQueue.GetSelectedCount() == 1) {
		CMenuItemInfo mi;
		mi.fMask = MIIM_DATA;
		
		pmMenu.GetMenuItemInfo(wID, FALSE, &mi);
		QueueItem::Source* s = (QueueItem::Source*)mi.dwItemData;
		PrivateFrame::openWindow(s->getUser(), m_hWndMDIClient, getTab());
	}
	return 0;
}
	
LRESULT QueueFrame::onPriority(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	QueueItem::Priority p;

	switch(wID) {
		case IDC_PRIORITY_PAUSED: p = QueueItem::PAUSED; break;
		case IDC_PRIORITY_LOWEST: p = QueueItem::LOWEST; break;
		case IDC_PRIORITY_LOW: p = QueueItem::LOW; break;
		case IDC_PRIORITY_NORMAL: p = QueueItem::NORMAL; break;
		case IDC_PRIORITY_HIGH: p = QueueItem::HIGH; break;
		case IDC_PRIORITY_HIGHEST: p = QueueItem::HIGHEST; break;
		default: p = QueueItem::DEFAULT; break;
	}

	if(usingDirMenu) {
		setPriority(ctrlDirs.GetSelectedItem(), p);
	} else {
		int i = -1;
		while( (i = ctrlQueue.GetNextItem(i, LVNI_SELECTED)) != -1) {
			QueueManager::getInstance()->setPriority(((QueueItem*)ctrlQueue.GetItemData(i))->getTarget(), p);
		}
	}

	return 0;
}

void QueueFrame::removeDir(HTREEITEM ht) {
	if(ht == NULL)
		return;
	HTREEITEM child = ctrlDirs.GetChildItem(ht);
	while(child) {
		removeDir(child);
		child = ctrlDirs.GetNextSiblingItem(child);
	}
	const string& name = getDir(ht);
	DirectoryPair dp = directories.equal_range(name);
	for(DirectoryIter i = dp.first; i != dp.second; ++i) {
		QueueManager::getInstance()->remove(i->second->getTarget());
	}
}

void QueueFrame::setPriority(HTREEITEM ht, const QueueItem::Priority& p) {
	if(ht == NULL)
		return;
	HTREEITEM child = ctrlDirs.GetChildItem(ht);
	while(child) {
		setPriority(child, p);
		child = ctrlDirs.GetNextSiblingItem(child);
	}
	string name = getDir(ht);
	DirectoryPair dp = directories.equal_range(name);
	for(DirectoryIter i = dp.first; i != dp.second; ++i) {
		QueueManager::getInstance()->setPriority(i->second->getTarget(), p);
	}
}

void QueueFrame::updateStatus() {
	if(ctrlStatus.IsWindow()) {
		int64_t total = 0;
		int cnt = ctrlQueue.GetSelectedCount();
		if(cnt == 0) {
			cnt = ctrlQueue.GetItemCount();
			if(showTree) {
				for(int i = 0; i < cnt; ++i) {
					QueueItem* qi = (QueueItem*)ctrlQueue.GetItemData(i);
					total += (qi->getSize() > 0) ? qi->getSize() : 0;
				}
			} else {
				total = queueSize;
			}
		} else {
			int i = -1;
			while( (i = ctrlQueue.GetNextItem(i, LVNI_SELECTED)) != -1) {
				QueueItem* qi = (QueueItem*)ctrlQueue.GetItemData(i);
				total += (qi->getSize() > 0) ? qi->getSize() : 0;
			}

		}

		string tmp1 = STRING(ITEMS) + ": " + Util::toString(cnt);
		string tmp2 = STRING(SIZE) + ": " + Util::formatBytes(total);
		bool u = false;

		int w = WinUtil::getTextWidth(tmp1, ctrlStatus.m_hWnd);
		if(statusSizes[1] < w) {
			statusSizes[1] = w;
			u = true;
		}
		ctrlStatus.SetText(2, tmp1.c_str());
		w = WinUtil::getTextWidth(tmp2, ctrlStatus.m_hWnd);
		if(statusSizes[2] < w) {
			statusSizes[2] = w;
			u = true;
		}
		ctrlStatus.SetText(3, tmp2.c_str());

		if(dirty) {
			tmp1 = STRING(FILES) + ": " + Util::toString(queueItems);
			tmp2 = STRING(SIZE) + ": " + Util::formatBytes(queueSize);

			w = WinUtil::getTextWidth(tmp2, ctrlStatus.m_hWnd);
			if(statusSizes[3] < w) {
				statusSizes[3] = w;
				u = true;
			}
			ctrlStatus.SetText(4, tmp1.c_str());

			w = WinUtil::getTextWidth(tmp2, ctrlStatus.m_hWnd);
			if(statusSizes[4] < w) {
				statusSizes[4] = w;
				u = true;
			}
			ctrlStatus.SetText(5, tmp2.c_str());

			dirty = false;
		}

		if(u)
			UpdateLayout(TRUE);
	}
}

void QueueFrame::UpdateLayout(BOOL bResizeBars /* = TRUE */) {
	RECT rect;
	GetClientRect(&rect);
	// position bars and offset their dimensions
	UpdateBarsPosition(rect, bResizeBars);

	if(ctrlStatus.IsWindow()) {
		CRect sr;
		int w[6];
		ctrlStatus.GetClientRect(sr);
		w[5] = sr.right - 16;
#define setw(x) w[x] = max(w[x+1] - statusSizes[x], 0)
		setw(4); setw(3); setw(2); setw(1);

		w[0] = 16;

		ctrlStatus.SetParts(6, w);

		ctrlStatus.GetRect(0, sr);
		ctrlShowTree.MoveWindow(sr);
	}

	if(showTree) {
		if(GetSinglePaneMode() != SPLIT_PANE_NONE) {
			SetSinglePaneMode(SPLIT_PANE_NONE);
			updateQueue();
		}
	} else {
		if(GetSinglePaneMode() != SPLIT_PANE_RIGHT) {
			SetSinglePaneMode(SPLIT_PANE_RIGHT);
			updateQueue();
		}
	}
	
	CRect rc = rect;
	SetSplitterRect(rc);
}

LRESULT QueueFrame::onClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) {
	if(!closed) {
		QueueManager::getInstance()->removeListener(this);

		closed = true;
		PostMessage(WM_CLOSE);
		return 0;
	} else {
		QueueFrame::frame = NULL;

		HTREEITEM ht = ctrlDirs.GetRootItem();
		while(ht != NULL) {
			clearTree(ht);
			ht = ctrlDirs.GetNextSiblingItem(ht);
		}

		SettingsManager::getInstance()->set(SettingsManager::QUEUEFRAME_SHOW_TREE, ctrlShowTree.GetCheck() == BST_CHECKED);
		{
			Lock l(cs);
			for(QueueIter i = queue.begin(); i != queue.end(); ++i) {
				delete i->second;
			}
			queue.clear();
		}
		ctrlQueue.DeleteAllItems();

		WinUtil::saveHeaderOrder(ctrlQueue, SettingsManager::QUEUEFRAME_ORDER, 
			SettingsManager::QUEUEFRAME_WIDTHS, COLUMN_LAST, columnIndexes, columnSizes);

		MDIDestroy(m_hWnd);
		return 0;
	}
}

LRESULT QueueFrame::onItemChanged(int /*idCtrl*/, LPNMHDR /* pnmh */, BOOL& /*bHandled*/) {
	updateQueue();
	return 0;
}

void QueueFrame::updateQueue() {
	Lock l(cs);

	ctrlQueue.DeleteAllItems();
	pair<DirectoryIter, DirectoryIter> i;
	if(showTree) {
		i = directories.equal_range(getSelectedDir());
	} else {
		i.first = directories.begin();
		i.second = directories.end();
	}

	ctrlQueue.SetRedraw(FALSE);
	for(DirectoryIter j = i.first; j != i.second; ++j) {
		QueueItem* qi = j->second;

		StringList sl;
		StringListInfo li(qi);
		for(int k = 0; k < COLUMN_LAST; k++) {
			sl.push_back(li.columns[k]);
		}
		
		ctrlQueue.insert(ctrlQueue.GetItemCount(), sl, WinUtil::getIconIndex(qi->getTarget()), (LPARAM)qi);
	}
	ctrlQueue.SetRedraw(TRUE);
	ctrlQueue.resort();
	curDir = getSelectedDir();
	updateStatus();
}

// Put it here to avoid a copy for each recursion...
static char tmpBuf[1024];
void QueueFrame::moveNode(HTREEITEM item, HTREEITEM parent) {
	TVINSERTSTRUCT tvis;
	memset(&tvis, 0, sizeof(tvis));
	tvis.itemex.hItem = item;
	tvis.itemex.mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM |
		TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT;
	tvis.itemex.pszText = tmpBuf;
	tvis.itemex.cchTextMax = 1024;
	ctrlDirs.GetItem((TVITEM*)&tvis.itemex);
	tvis.hInsertAfter =	TVI_SORT;
	tvis.hParent = parent;
	HTREEITEM ht = ctrlDirs.InsertItem(&tvis);
	HTREEITEM next = ctrlDirs.GetChildItem(item);
	while(next != NULL) {
		moveNode(next, ht);
		next = ctrlDirs.GetChildItem(item);
	}
	ctrlDirs.DeleteItem(item);
}

void QueueFrame::onAction(QueueManagerListener::Types type, QueueItem* aQI) throw() { 
	switch(type) {
	case QueueManagerListener::ADDED: onQueueAdded(aQI); break;
	case QueueManagerListener::QUEUE_ITEM: onQueueAdded(aQI); onQueueUpdated(aQI); break;
	case QueueManagerListener::REMOVED: onQueueRemoved(aQI); break;
	case QueueManagerListener::MOVED: onQueueMoved(aQI); break;
	case QueueManagerListener::SOURCES_UPDATED: onQueueUpdated(aQI); break;
	case QueueManagerListener::STATUS_UPDATED: onQueueUpdated(aQI); break;
	default: break;
	}
};

/**
 * @file
 * $Id: QueueFrame.cpp,v 1.24 2003/07/15 14:53:12 arnetheduck Exp $
 */